package smallfrench.moteur.biblio.igu;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.LinkedList;

import javax.swing.JPanel;

import smallfrench.moteur.biblio.AClasse;
import smallfrench.moteur.biblio.AVecteur2;
import smallfrench.moteur.biblio.AnnotationMembre;
import smallfrench.moteur.biblio.RChaine;
import smallfrench.moteur.biblio.RFlottant;

/**
 * Un graphique pour tracer des {@link Courbe}s.
 * 
 * @author antoine1023
 */
public class Graphique extends Composant {

	public static final AClasse CLASSE = AClasse.creer(Graphique.class);

	/**
	 * La vitesse de déplacement en pixel
	 */
	private static final double VITESSE_DEPLACEMENT = 40.0;

	private Panel panel;

	private LinkedList<Courbe> courbes;

	private AVecteur2 centre = new AVecteur2(0.0, 0.0);

	/** Nombre de pixels par unité */
	private RFlottant zoom = new RFlottant(10.0);

	//-------------------------------------------------------------------------

	@AnnotationMembre
	public Graphique() {
		panel = new Panel(this);
		super.setComponent(panel);
		courbes = new LinkedList<Courbe>();
	}

	//-------------------------------------------------------------------------

	@AnnotationMembre
	public void ajouterCourbe(Courbe courbe) {
		courbes.add(courbe);
	}

	@Override
	public Container getComponent() {
		return panel;
	}

	@Override
	@AnnotationMembre
	public RChaine enChaine() {
		return new RChaine(CLASSE.nom);
	}

	//-------------------------------------------------------------------------

	@SuppressWarnings("serial")
	private static class Panel extends JPanel implements MouseWheelListener {

		private Graphique graphique;

		public Panel(Graphique graphique) {
			this.graphique = graphique;

			addMouseWheelListener(this);
		}

		@Override
		public void paint(Graphics graphics) {

			Graphics2D g = (Graphics2D) graphics;
			g.setRenderingHint(
					RenderingHints.KEY_TEXT_ANTIALIASING,
					RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
			g.setRenderingHint(
					RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);

			g.setColor(getBackground());
			g.fillRect(0, 0, getWidth(), getHeight());

			double zoom = graphique.zoom.valeur;

			Transformation t = new Transformation(
					zoom, 0,
					0, zoom,
					0, 0);

			AVecteur2 tailleFenetre = new AVecteur2(
					getWidth() / zoom,
					getHeight() / zoom);

			AVecteur2 posFenetre = new AVecteur2(
					graphique.centre.x - tailleFenetre.x / 2.0,
					graphique.centre.y - tailleFenetre.y / 2.0);

			//------------------------

			for (Courbe i : graphique.courbes) {
				i.dessiner(g, getWidth(), getHeight(),
						posFenetre.x, posFenetre.y,
						t);
			}

			//------------------------ Affichage des axes

			AVecteur2 origine = t.transformer(
					AVecteur2.NUL.soustraire(posFenetre));

			int ox = (int) origine.x;
			int oy =  getHeight() - (int)origine.y;

			g.setStroke(new BasicStroke((float) 0.5));
			g.setColor(Color.BLACK);

			g.drawLine(0, oy, getWidth(), oy);
			g.drawLine(ox, 0, ox, getHeight());

			//------------------------ Affichage des graduations

			if (zoom < 2.0)
				// Les graduations seraint trop petites
				return;

			// Début des graduations sur les axes
			double debutA = posFenetre.x % 1.0,
					debutO = posFenetre.y % 1.0;

			for (int i = 0; i < tailleFenetre.x; i++) {
				g.fillRect((int) ((i - debutA) * zoom), oy - 3,
						1, 3);
			}
			for (int i = 0; i < tailleFenetre.y; i++) {
				g.fillRect(ox, (int) ((i - debutO) * zoom),
						3, 1);
			}
		}

		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {

			int unites = e.getUnitsToScroll();
			double z = graphique.zoom.valeur;

			if (e.isControlDown()) {
				if (unites > 0) {
					graphique.zoom = new RFlottant(z / 1.1);
				} else {
					graphique.zoom = new RFlottant(z * 1.1);
				}
			} else if (e.isShiftDown()) {
				if (unites > 0) {
					graphique.centre = graphique.centre.ajouter(
							VITESSE_DEPLACEMENT / -z, 0.0);
				} else {
					graphique.centre = graphique.centre.ajouter(
							VITESSE_DEPLACEMENT / z, 0.0);
				}
			} else {
				if (unites > 0) {
					graphique.centre = graphique.centre.ajouter(
							0.0, VITESSE_DEPLACEMENT / -z);
				} else {
					graphique.centre = graphique.centre.ajouter(
							0.0, VITESSE_DEPLACEMENT / z);
				}
			}

			repaint();
		}
	}

}
